#include "TextureRender.h"
#include "base/SnImage.h"
#include "base/shader.h"
#include <algorithm>
#include <stdint.h>
#include <vcruntime_string.h>

namespace Sivin {
  TextureRender::TextureRender() {

    mPositionVertexData = {
        0.5f, 0.5f, 0.0f,  // 右上
        -0.5f, 0.5f, 0.0f, // 左上
        -0.5f, -0.5f, 0.0f,// 左下
        0.5f, -0.5f, 0.0f};// 右下

    mColorVertexData = {
        1.0f, 0.0f, 0.0f,
        0.0f, 1.0f, 0.0f,
        0.0f, 0.0f, 1.0f,
        1.0f, 1.0f, 0.0f};

    //1.0->2.0相当于四张图片，
    mTextureCoord = {
        2.0f, 2.0f,
        0.0f, 2.0f,
        0.0f, 0.0f,
        2.0f, 0.0f};


    mElementIndexArray = {0, 1, 2, // 第一个三角形
                          0, 2, 3};// 第二个三角形


    mShader = std::make_unique<Shader>("./../shader/texture/vert.vs", "./../shader/texture/frag.fs");
  }

  TextureRender::~TextureRender() {
  }

  /**
   * 
   * 这个例子演示了：单独指定VAO的过程，
   * 不一定需要绑定当前上下文，而是指定需要操作的VAO对象
   * 这个例子必须在在OpenGL4.5以上版本运行
   */
  void TextureRender::onGLInit() {
    mShader->init();
    initVAO();
    initTexture();
  }

  void TextureRender::onRender() {
    mShader->start();
    glBindTexture(GL_TEXTURE_2D, mTextureId);
    glBindVertexArray(mVAO);
    glDrawElements(GL_TRIANGLES, mElementIndexArray.size(), GL_UNSIGNED_INT, (void *) 0);
    mShader->end();
  }

  void TextureRender::initVAO() {
    glGenVertexArrays(1, &mVAO);
    //创建mVAO
    glBindVertexArray(mVAO);

    /**
     * 之前的例子，这里我们不能解绑VAO，否则就无法记录
     * 这个例子，我们用另一种方式对VAO传递参数
     */
    glBindVertexArray(0);

    GLuint posVbo, colorVbo, uvVbo, ebo;
    glGenBuffers(1, &posVbo);
    glGenBuffers(1, &colorVbo);
    glGenBuffers(1, &uvVbo);
    glGenBuffers(1, &ebo);

    glBindBuffer(GL_ARRAY_BUFFER, posVbo);
    glBufferData(GL_ARRAY_BUFFER, sizeof(GLfloat) * mPositionVertexData.size(), mPositionVertexData.data(), GL_STATIC_DRAW);
    glBindBuffer(GL_ARRAY_BUFFER, posVbo);

    glBindBuffer(GL_ARRAY_BUFFER, colorVbo);
    glBufferData(GL_ARRAY_BUFFER, sizeof(GLfloat) * mColorVertexData.size(), mColorVertexData.data(), GL_STATIC_DRAW);
    glBindBuffer(GL_ARRAY_BUFFER, 0);

    glBindBuffer(GL_ARRAY_BUFFER, uvVbo);
    glBufferData(GL_ARRAY_BUFFER, sizeof(GLfloat) * mTextureCoord.size(), mTextureCoord.data(), GL_STATIC_DRAW);
    glBindBuffer(GL_ARRAY_BUFFER, 0);

    glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, ebo);
    glBufferData(GL_ELEMENT_ARRAY_BUFFER, sizeof(GLuint) * mElementIndexArray.size(), mElementIndexArray.data(), GL_STATIC_DRAW);
    glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0);

    //为某个VAO单独指定EBO对象
    glVertexArrayElementBuffer(mVAO, ebo);

    GLuint buffers[] = {posVbo, colorVbo, uvVbo};
    const GLintptr offsets[] = {0, 0, 0};
    const GLsizei strides[] = {3 * sizeof(GLfloat), 3 * sizeof(GLfloat), 2 * sizeof(GLfloat)};

    /**
     * @param vaobj:表示顶点数组对象
     * @param first:shader中的开始location,后面依次递增
     * @param count:顶点属性的个数
     * @param buffers:顶点缓冲对象的数组地址
     * @param offsets:每个顶点属性数据的起始偏移
     * @param strides:每个顶点属性数据的步长
     */
    glVertexArrayVertexBuffers(mVAO, 0, sizeof(buffers) / sizeof(GLuint), buffers, offsets, strides);

    //指定启用顶点数组对象里的顶点属性
    glEnableVertexArrayAttrib(mVAO, 0);

    /**
     * 用于指定顶点数组中每个属性的格式
     * @param vaobj:指定的VAO对象
     * @param attribindex:属性索引
     * @param size:属性的分量个数
     * @param type:属性的分量类型
     * @param normalized:是否需要归一化
     * @param relativeoffset：属性在指定缓冲中的起始字节偏移
     */
    glVertexArrayAttribFormat(mVAO, 0, 3, GL_FLOAT, GL_FALSE, 0);

    glEnableVertexArrayAttrib(mVAO, 1);
    glVertexArrayAttribFormat(mVAO, 1, 3, GL_FLOAT, GL_FALSE, 0);

    glEnableVertexArrayAttrib(mVAO, 2);
    glVertexArrayAttribFormat(mVAO, 2, 2, GL_FLOAT, GL_FALSE, 0);
  }

  void TextureRender::initTexture() {

    glCreateTextures(GL_TEXTURE_2D, 1, &mTextureId);

    /**
     * 旧版本API需要提前绑定，因为需要关联上下文，因此第一个参数是GL_TEXTURE_2D
     * 新API用绑定到上下文就可以操作，第一个参数是mTextureId对象
     */
    // glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT);
    // glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT);
    // glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
    // glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);

    glTextureParameteri(mTextureId, GL_TEXTURE_WRAP_S, GL_REPEAT);
    glTextureParameteri(mTextureId, GL_TEXTURE_WRAP_T, GL_REPEAT);
    glTextureParameteri(mTextureId, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
    glTextureParameteri(mTextureId, GL_TEXTURE_MAG_FILTER, GL_LINEAR);

    glBindTexture(GL_TEXTURE_2D, mTextureId);
    auto mCarTongImg = SnImage::loadImage("./../res/catong.png");
    //这个API需要textureId绑定到上下文
    glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, mCarTongImg->width(), mCarTongImg->height(),
                 0, GL_RGB, GL_UNSIGNED_BYTE, mCarTongImg->data().get());

    bool enableMiMap = false;
    GLint filter;
    glGetTexParameteriv(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, &filter);
    if (filter == GL_NEAREST_MIPMAP_NEAREST || filter == GL_LINEAR_MIPMAP_NEAREST || filter == GL_NEAREST_MIPMAP_LINEAR || filter == GL_LINEAR_MIPMAP_LINEAR) {
      enableMiMap = true;
    } else {
      enableMiMap = false;
    }
    // !如果设置过mipmap过滤方式，这里必须生成mipmpap纹理，否则渲染失败
    if (enableMiMap) {
      glGenerateMipmap(GL_TEXTURE_2D);
    }
  }

}// namespace Sivin